﻿using Devil;
using UnityEngine;

namespace GameToolkit
{
    public enum EControllerType
    {
        Unknown,
        Keyboard = 0x100,
        Mouse = 0x200,
        //MouseAxis = 0x21,
        Controller = 0x300,
        //ControllerAxis = 0x31,

        //TypeMask = 0xFF0,
        //AxisMask = 0xF,
    }

    /// <summary>
    /// 自定义类型
    /// </summary>
    public enum ECustomizableKey
    {
        Customizable = 0, // 可自定义键
        SystemBack = 1, // 系统返回
        SystemStart = 2, // 系统开始
        Fallback = 3, // 保留按键
    }

    /// <summary>
    /// 输入设备（控制器，手柄，键盘等）
    /// </summary>
    public abstract class InputController : ScriptableObject
    {
        const int CUSTOMIZABLE_MASK = 0xF;
        const int AXIS_MASK = 0XF0;
        const int TYPE_MASK = 0XF00;

        /// <summary>
        /// 支持备用按键（key2）的配置
        /// </summary>
        public interface ISupportKey2
        {

            bool IsPreferAction(IInputAction action);
        }

        /// <summary>
        /// 捕获设备输入
        /// </summary>
        public interface IInputActionHook
        {
            /// <summary>
            /// 与设备连接
            /// </summary>
            /// <param name="controller"></param>
            void OnAttached(InputController controller);

            /// <summary>
            /// 获得设备输入
            /// </summary>
            /// <param name="key"></param>
            void OnGetAction(KeyInfo key);

            /// <summary>
            /// 与设备断开
            /// </summary>
            /// <param name="controller"></param>
            void OnDettached(InputController controller);
        }

        public enum EVibrationID
        {
            None = 0,
            Left = 1,
            Right = 2,
            Both = 3,
        }

        /// <summary>
        /// 输入设备震动控制
        /// </summary>
        public interface IInputVibrate
        {
            /// <summary>
            /// 开始震动
            /// </summary>
            /// <param name="id"></param>
            /// <param name="strength"></param>
            /// <param name="duration"></param>
            /// <returns></returns>
            int StartVibration(EVibrationID id, float strength, float duration);

            void AppendVibration(int id, float duration);

            void StopVibration(int id);
        }
        
        public struct KeyInfo : System.IEquatable<KeyInfo>
        {
            int flags;
            public EControllerType type { get { return (EControllerType)(flags & TYPE_MASK); } }
            public bool isAxis { get { return (flags & AXIS_MASK) != 0; } }
            public ECustomizableKey customizable { get { return (ECustomizableKey)(flags & CUSTOMIZABLE_MASK); } }
            public string keyName;
            public int keyCode;
            // 按键映射的硬件按键名
            public string rawKeyName
            {
                get
                {
                    if (string.IsNullOrEmpty(keyName))
                        return keyName;
                    var c = keyName[0];
                    if (c == '+' || c == '-')
                        return keyName.Substring(1);
                    else
                        return keyName;
                }
            }

            public bool isValid { get { return type != 0 && !string.IsNullOrEmpty(keyName); } }

            public KeyInfo(EControllerType type, bool isAxis, ECustomizableKey customizable, string name, int code)
            {
                flags = ((int)type & TYPE_MASK) | (isAxis ? AXIS_MASK : 0) | ((int)(customizable) & CUSTOMIZABLE_MASK); 
                this.keyName = name;
                this.keyCode = code;
            }

            public bool Equals(KeyInfo other)
            {
                return this.type == other.type && this.keyCode == other.keyCode && this.keyName.EqualsIgnoreCase(other.keyName);
            }

            public static KeyInfo GetAxis(EControllerType type, string name, int code)
            {
                KeyInfo key;
                var custom = type == EControllerType.Controller ? ECustomizableKey.Customizable : ECustomizableKey.Fallback;
                key.flags = ((int)type & TYPE_MASK) | AXIS_MASK | (int)custom;
                key.keyName = name;
                key.keyCode = code;
                return key;
            }

            public static KeyInfo GetButton(EControllerType type, string name, int code)
            {
                KeyInfo key;
                key.flags =  (int)type & TYPE_MASK;
                key.keyName = name;
                key.keyCode = code;
                return key;
            }

            public static KeyInfo GetButton(EControllerType type, ECustomizableKey customizable, string name, int code)
            {
                KeyInfo key;
                key.flags = ((int)type & TYPE_MASK) | ((int)customizable & CUSTOMIZABLE_MASK);
                key.keyName = name;
                key.keyCode = code;
                return key;
            }
        }

        public interface IInputAction
        {
            InputController Controller { get; }
            EControllerType ControllerType { get; }
            float Value { get; }
            bool HasButton { get; }
            KeyInfo GetKeyInfo();
        }

#pragma warning disable CS0649
        [SerializeField]
        bool m_IsDefault;
        [SerializeField]
        bool m_AsFallback;
#pragma warning restore

        public bool IsDefault { get { return m_IsDefault; } }
        public int TypeIdentifier { get; private set; }
        public string ConfigName { get; private set; }
        public bool IsConnected { get; private set; }
        public abstract EControllerType ControllerType { get; }
        //public abstract bool IsAnyKeyDown { get; }
        public abstract bool IsKindOf(EControllerType type);
        public abstract KeyInfo[] GetKeysInfo();
        public abstract IInputAction GetAction(string keyName);
        /// <summary>
        /// 测试连接状态
        /// </summary>
        /// <returns></returns>
        protected abstract bool TestConnection();
        public virtual void Prepare(GameInput ginput)
        {
            this.ConfigName = name;
            //IsConnected = false;
            this.TypeIdentifier = ConfigName.IgnoreCaseToHash();
        }

        public virtual void Release(GameInput ginput) { }

        /// <summary>
        /// 检查连接状态是否改变
        /// </summary>
        /// <returns></returns>
        internal bool ValidateConnectionChanged(bool asFallback)
        {
            var wasConnect = IsConnected;
            if (asFallback)
                IsConnected = m_AsFallback || TestConnection();
            else
                IsConnected = TestConnection();
            return wasConnect != IsConnected;
        }

    }

}